//
//  CXMLDocument.m
//  VmixXml
//
//  Created by Lei Pan on 5/26/10.
//  Copyright 2010 VMIX Media. All rights reserved.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//  OTHER DEALINGS IN THE SOFTWARE.

#import "CXMLDocument.h"

#include <libxml/parser.h>

#import "CXMLNode_PrivateExtensions.h"
#import "CXMLElement.h";

@implementation CXMLDocument

- (id)initWithXMLString:(NSString *)inString options:(NSUInteger)inOptions error:(NSError **)outError
{
if ((self = [super init]) != NULL)
	{
	NSError *theError = NULL;
	
	/*#if TOUCHXMLUSETIDY
	if (inOptions & CXMLDocumentTidyHTML)
		{
		inString = [[CTidy tidy] tidyString:inString inputFormat:TidyFormat_HTML outputFormat:TidyFormat_XHTML diagnostics:NULL error:&theError];
		}
	else if (inOptions & CXMLDocumentTidyXML)
		{
		inString = [[CTidy tidy] tidyString:inString inputFormat:TidyFormat_XML outputFormat:TidyFormat_XML diagnostics:NULL error:&theError];
		}
	#endif
	*/
	xmlDocPtr theDoc = xmlParseDoc((xmlChar *)[inString UTF8String]);
	if (theDoc != NULL)
		{
		_node = (xmlNodePtr)theDoc;
		NSAssert(_node->_private == NULL, @"TODO");
		_node->_private = self; // Note. NOT retained (TODO think more about _private usage)
		}
	else
		{
		xmlErrorPtr	theLastErrorPtr = xmlGetLastError();
		
		NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
			[NSString stringWithUTF8String:theLastErrorPtr->message], NSLocalizedDescriptionKey,
			NULL];
		
		
		theError = [NSError errorWithDomain:@"CXMLErrorDomain" code:1 userInfo:theUserInfo];
		
		xmlResetLastError();
		}

	if (outError)
		*outError = theError;

	if (theError != NULL)
		{
		[self release];
		self = NULL;
		}
	}
return(self);
}

- (id)initWithData:(NSData *)inData options:(NSUInteger)inOptions error:(NSError **)outError
{
	return [self initWithData:inData encoding:NSUTF8StringEncoding options:inOptions error:outError];	 
}

- (id)initWithData:(NSData *)inData encoding:(NSStringEncoding)encoding options:(NSUInteger)inOptions error:(NSError **)outError
{
	if ((self = [super init]) != NULL)
	{
		NSError *theError = NULL;
		
		/*#if TOUCHXMLUSETIDY
		if (inOptions & CXMLDocumentTidyHTML)
		{
			inData = [[CTidy tidy] tidyData:inData inputFormat:TidyFormat_HTML outputFormat:TidyFormat_XHTML diagnostics:NULL error:&theError];
		}
		else if (inOptions & CXMLDocumentTidyXML)
		{
			inData = [[CTidy tidy] tidyData:inData inputFormat:TidyFormat_XML outputFormat:TidyFormat_XML diagnostics:NULL error:&theError];
		}
		#endif
		*/
		if (theError == NULL)
		{
			xmlDocPtr theDoc = NULL;
			if (inData && inData.length > 0)
			{
				CFStringEncoding cfenc = CFStringConvertNSStringEncodingToEncoding(encoding);
				CFStringRef cfencstr = CFStringConvertEncodingToIANACharSetName(cfenc);
				const char *enc = CFStringGetCStringPtr(cfencstr, 0);
				theDoc = xmlReadMemory([inData bytes], [inData length], NULL, enc, XML_PARSE_RECOVER | XML_PARSE_NOWARNING);
			}
			
			if (theDoc != NULL)
			{
				_node = (xmlNodePtr)theDoc;
				_node->_private = self; // Note. NOT retained (TODO think more about _private usage)
			}
			else
			{
				theError = [NSError errorWithDomain:@"CXMLErrorDomain" code:-1 userInfo:NULL];
			}
		}
		
		if (outError)
			*outError = theError;
		
		if (theError != NULL)
		{
			[self release];
			self = NULL;
		}
	}
	return(self);
}

- (id)initWithContentsOfURL:(NSURL *)inURL options:(NSUInteger)inOptions error:(NSError **)outError
{
	return [self initWithContentsOfURL:inURL encoding:NSUTF8StringEncoding options:inOptions error:outError];
}

- (id)initWithContentsOfURL:(NSURL *)inURL encoding:(NSStringEncoding)encoding options:(NSUInteger)inOptions error:(NSError **)outError
{
	if (outError)
		*outError = NULL;
	
	NSData *theData = [NSData dataWithContentsOfURL:inURL options:NSUncachedRead error:outError];
	if (theData)
	{
		self = [self initWithData:theData encoding:encoding options:inOptions error:outError];
	}
	else
	{
		self = NULL;
	}
	
	return(self);
}


- (void)dealloc
{
	// Fix for #35 http://code.google.com/p/touchcode/issues/detail?id=35 -- clear up the node objects first (inside a pool so I _know_ they're cleared) and then freeing the document
	
	NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
	
	[nodePool release];
	nodePool = NULL;
	
	[thePool release];
	//
	xmlFreeDoc((xmlDocPtr)_node);
	_node = NULL;
	//
	[super dealloc];
}

//- (NSString *)characterEncoding;
//- (NSString *)version;
//- (BOOL)isStandalone;
//- (CXMLDocumentContentKind)documentContentKind;
//- (NSString *)MIMEType;
//- (CXMLDTD *)DTD;

- (CXMLElement *)rootElement
{
	xmlNodePtr theLibXMLNode = xmlDocGetRootElement((xmlDocPtr)_node);
	
	return([CXMLNode nodeWithLibXMLNode:theLibXMLNode]);
}

- (NSData *)XMLData
{
	return([self XMLDataWithOptions:0]);
}

- (NSData *)XMLDataWithOptions:(NSUInteger)options
{
	xmlChar *theBuffer = NULL;
	int theBufferSize = 0;
	xmlDocDumpMemory((xmlDocPtr)self->_node, &theBuffer, &theBufferSize);
	
	NSData *theData = [NSData dataWithBytes:theBuffer length:theBufferSize];
	
	xmlFree(theBuffer);
	
	return(theData);
}

- (id)XMLStringWithOptions:(NSUInteger)options
{
	id root = [self rootElement];
	NSMutableString* xmlString = [NSMutableString string];
	[root _XMLStringWithOptions:options appendingToString:xmlString];
	return xmlString;
}

- (NSString *)description
{
	NSAssert(_node != NULL, @"TODO");
	
	NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p [%p]> ", NSStringFromClass([self class]), self, self->_node];
	xmlChar *xmlbuff;
	int buffersize;
	
	xmlDocDumpFormatMemory((xmlDocPtr)(self->_node), &xmlbuff, &buffersize, 1);
	NSString *dump = [[[NSString alloc] initWithBytes:xmlbuff length:buffersize encoding:NSUTF8StringEncoding] autorelease];
	xmlFree(xmlbuff);
	
	[result appendString:dump];
	return result;
}

@end
